#include "apdu_manager.h"

#include "../../mk_lib/mk_utility.h"
#include "../apdu.algo/sha1.h"
#include "../apdu.algo/mac.h"
#include "../apdu.algo/sm4.h"
#include <string.h>
#include <memory.h>
#include <stdlib.h>

apdu_manager::apdu_manager(void)
{
}

apdu_manager::~apdu_manager(void)
{
}



apdu * apdu_manager::create_apdu_create_application(unsigned char * app_settings, int data_len)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x20, 0x00, 0x00, "CreateApplication");
	newApdu->set_lc_data(app_settings, data_len);

	return newApdu;
}


apdu * apdu_manager::create_apdu_update_application(int app_id, unsigned char * app_settings, int data_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0xC0, 0x62, 0x00, 0x00, "UpdateApplication");

	unsigned char id[2] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, id);

	unsigned char cmd[1024] = {0};
	int pos = 0;

	memcpy(cmd, id, 2);
	pos +=2; 

	memcpy(cmd + pos, app_settings, data_len);
	pos += data_len;

	newApdu->set_lc_data(cmd, pos);

	return newApdu;
}

apdu * apdu_manager::create_apdu_enum_application()
{
	apdu * newApdu = new apdu(SC_APDU_CASE_2_EXT, 0x80, 0x22, 0x00, 0x00, "EnumApplication");

	return newApdu;
}

apdu * apdu_manager::create_apdu_delete_application(char * app_name)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x24, 0x00, 0x00, "DeleteApplication");
	newApdu->set_lc_data((unsigned char*)app_name, strlen(app_name));
	newApdu->set_comm_timeout(10000);

	return newApdu;
}

apdu * apdu_manager::create_apdu_open_application(char * app_name)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x26, 0x00, 0x00, "OpenApplication");
	newApdu->set_lc_data((unsigned char*)app_name, strlen(app_name));
	newApdu->set_le(10);

	return newApdu;
}

apdu * apdu_manager::create_apdu_close_application(int app_id)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x28, 0x00, 0x00, "CloseApplication");

	unsigned char id[2] = {0};

	mk_utility::fill_buff_with_word_value_be(app_id, id);
	newApdu->set_lc_data(id, 2);

	return newApdu;
}

apdu * apdu_manager::create_apdu_get_ext_app_info( int app_id )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0xC0, 0x60, 0x00, 0x00, "GetAppExtenedInfo");

	unsigned char id[2] = {0};

	mk_utility::fill_buff_with_word_value_be(app_id, id);
	newApdu->set_lc_data(id, 2);

	return newApdu;
}

apdu * apdu_manager::create_apdu_create_file(int app_id, unsigned char * file_attrs, int attrs_len)
{	
	unsigned char id[2] = {0};
	memcpy(id, &app_id, 2);

	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x30, id[1], id[0], "CreateFile");
	newApdu->set_lc_data(file_attrs, attrs_len);

	return newApdu;
}

apdu * apdu_manager::create_apdu_delete_file(int app_id, char * file_name)
{
	unsigned char id[2] = {0};
	memcpy(id, &app_id, 2);

	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x32, id[1], id[0], "DeleteFile");
	newApdu->set_lc_data((unsigned char*)file_name, strlen(file_name));
	newApdu->set_comm_timeout(10000); //more time are required for big file.


	return newApdu;
}

apdu * apdu_manager::create_apdu_enum_files(int app_id)
{
	unsigned char id[2] = {0};
	memcpy(id, &app_id, 2);

	apdu * newApdu = new apdu(SC_APDU_CASE_2_EXT, 0x80, 0x34, id[1], id[0], "EnumFiles");
	

	return newApdu;
}

apdu * apdu_manager::create_apdu_get_file_info(int app_id, char * file_name)
{
	unsigned char id[2] = {0};
	memcpy(id, &app_id, 2);

	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x36, id[1], id[0], "GetFileInfo");
	newApdu->set_lc_data((unsigned char*)file_name, strlen(file_name));
	newApdu->set_le(12);

	return newApdu;
}

apdu * apdu_manager::create_apdu_read_file(int app_id, int offset, int read_len, char * file_name)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x38, 0x00, 0x00, "ReadFile");
	
	int pos = 0;
	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;
	
	mk_utility::fill_buff_with_word_value_be(offset, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(read_len, data + pos);
	pos += 2;

	int name_len = strlen(file_name);
	mk_utility::fill_buff_with_word_value_be(name_len, data + pos);
	pos += 2;

	memcpy(data + pos, file_name, name_len);
	pos += name_len;


	newApdu->set_lc_data(data, pos);
	newApdu->set_le(read_len);

	return newApdu;	
}

apdu * apdu_manager::create_apdu_write_file(int app_id, int offset, char * file_name,
											unsigned char * data, int data_len)
{	
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x3A, 0x00, 0x00, "WriteFile");
	
	unsigned char * pBuff = (unsigned char*)malloc(data_len + 128);
	int pos = 0;

	mk_utility::fill_buff_with_word_value_be(app_id, pBuff);
	pos +=2;

	mk_utility::fill_buff_with_word_value_be(offset, pBuff + pos);
	pos += 2;

	int name_len = strlen(file_name);
	mk_utility::fill_buff_with_word_value_be(name_len, pBuff + pos);
	pos += 2;

	memcpy(pBuff + pos, file_name, name_len);
	pos += name_len;

	mk_utility::fill_buff_with_word_value_be(data_len, pBuff + pos);
	pos += 2;

	memcpy(pBuff + pos, data, data_len);
	pos += data_len;

	newApdu->set_lc_data(pBuff, pos);

	free(pBuff);

	return newApdu;  

}

apdu * apdu_manager::create_apdu_set_file_info( int app_id, char * file_name,
											   unsigned char * file_attrs, int attrs_len)
{
	unsigned char id[2] = {0};
	memcpy(id, &app_id, 2);

	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0xC0, 0x50, id[1], id[0], "SetFileInfo");
	
	newApdu->set_lc_data(file_attrs, attrs_len);
	
	return newApdu;
}


apdu * apdu_manager::create_apdu_create_container(int app_id, char * container_name)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x40, 0x00, 0x00, "CreateContainer");

	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	
	int pos = 2;
	memcpy(data + pos, container_name, strlen(container_name));
	pos += strlen(container_name);

	newApdu->set_lc_data(data, pos);
	newApdu->set_le(2);

	return newApdu;
}

apdu * apdu_manager::create_apdu_open_container(int app_id, char * container_name)
{
	unsigned char id[2] = {0};
	memcpy(id, &app_id, 2);

	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x42, 0x00, 0x00, "OpenContainer");
	
	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);

	int pos = 2;
	memcpy(data + pos, container_name, strlen(container_name));
	pos += strlen(container_name);

	newApdu->set_lc_data(data, pos);
	newApdu->set_le(2);

	return newApdu;
}

apdu * apdu_manager::create_apdu_close_container(int app_id, int container_id)
{
	unsigned char id[2] = {0};
	memcpy(id, &app_id, 2);

	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x44, 0x00, 0x00, "CloseContainer");

	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);

	int pos = 2;
	mk_utility::fill_buff_with_word_value_be(container_id, data + 2);
	pos += 2;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_manager::create_apdu_enum_container(int app_id)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x46, 0x00, 0x00, "EnumContainers");

	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);

	newApdu->set_lc_data(data, 2);

	return newApdu;
}

apdu * apdu_manager::create_apdu_delete_container(int app_id, char * container_name)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x48, 0x00, 0x00, "DeleteContainer");

	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);

	int pos = 2;
	memcpy(data + pos, container_name, strlen(container_name));
	pos += strlen(container_name);

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_manager::create_apdu_get_containter_info(int app_id, char * container_name)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x4A, 0x00, 0x00, "GetContainerInfo");

	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);

	int pos = 2;
	memcpy(data + pos, container_name, strlen(container_name));
	pos += strlen(container_name);

	newApdu->set_lc_data(data, pos);
	newApdu->set_le(0x0B);

	return newApdu;
}

apdu * apdu_manager::create_apdu_get_container_name_by_id(int app_id, int container_id)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0xC0, 0x74, 0x00, 0x00, "GetContainerName");

	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	mk_utility::fill_buff_with_word_value_be(container_id, data + 2);

	newApdu->set_lc_data(data, 4);

	return newApdu;
}


apdu * apdu_manager::create_apdu_import_certificate_head(int app_id, int container_id, int cert_type, unsigned long total_len)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x4C, 0x00, 0x00,"ImportCertificate");
	
	unsigned char data[128];

	int pos = 0;
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	data[pos++] = (unsigned char)cert_type;
	mk_utility::fill_buff_with_dword_value_be(total_len, data + pos);
	pos += 4;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_manager::create_apdu_export_certificate(int app_id, int containter_id, int cert_type)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x4E, cert_type, 0x00, "ExportCertificate");
	
	int pos = 0;
	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(containter_id, data + pos);
	pos += 2;

	newApdu->set_lc_data(data, pos);
	return newApdu;
}

apdu * apdu_manager::create_apdu_gen_random(int len, int saveToFile)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_2_EXT, 0x80, 0x50, saveToFile, 0x00, "GenRandom");
	newApdu->set_le(len);

	return newApdu;
}

apdu * apdu_manager::create_apdu_set_current_file_pos( int pos )
{
	unsigned char para[2] = {0};
	memcpy(para, &pos, 2);

	apdu * newApdu = new apdu(SC_APDU_CASE_1, 0xC0, 0x52, para[1], para[0],"SetmFSCursor");

	return newApdu;
}

apdu * apdu_manager::create_apdu_get_container_ex_info( int app_id, int container_id, int offset, int data_len )
{
	unsigned char para[2] = {0};
	memcpy(para, &offset, 2);

	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0xC0, 0x72, para[1], para[0],"GetContainerExtInfo");

	int pos = 0;
	unsigned char cmd[128] = {0};

	mk_utility::fill_buff_with_word_value_be(app_id, cmd);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, cmd + pos);
	pos += 2;

	newApdu->set_lc_data(cmd, pos);
	newApdu->set_le(data_len);

	return newApdu;	
}

apdu * apdu_manager::create_apdu_set_container_ex_info( int app_id, int container_id, int offset, unsigned char * pData, int data_len )
{
	unsigned char para[2] = {0};
	memcpy(para, &offset, 2);

	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0xC0, 0x70, para[1], para[0],"SetContainerExtInfo");

	int pos = 0;
	unsigned char * pCmd = (unsigned char*)malloc(data_len + 128);
	mk_utility::fill_buff_with_word_value_be(app_id, pCmd);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, pCmd + pos);
	pos += 2;

	memcpy(pCmd + pos, pData, data_len);
	pos += data_len;

	newApdu->set_lc_data(pCmd, pos);

	free(pCmd);

	return newApdu;
}

apdu * apdu_manager::create_apdu_crypt_read_file( int app_id, int container_id, int key_id, 
												 int offset, int read_len, char * file_name, int is_final )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0xC0, 0x54, is_final, 0x00, "CryptReadFile");

	int pos = 0;
	unsigned char data[256] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(offset, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(read_len, data + pos);
	pos += 2;

	int name_len = strlen(file_name);
	mk_utility::fill_buff_with_word_value_be(name_len, data + pos);
	pos += 2;

	memcpy(data + pos, file_name, name_len);
	pos += name_len;

	mk_utility::fill_buff_with_word_value_be(app_id, data + pos);
	pos += 2;
	
	mk_utility::fill_buff_with_word_value_be(container_id, data + pos);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(key_id, data + pos);
	pos += 2;

	newApdu->set_lc_data(data, pos);
	newApdu->set_le(read_len);

	return newApdu;	
}

apdu * apdu_manager::create_apdu_crypt_write_file( int app_id,int container_id, int key_id, 
												  int offset, char * file_name, unsigned char * data, int data_len, int is_final)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0xC0, 0x56, is_final, 0x00, "CryptWriteFile");

	unsigned char * pBuff = (unsigned char*)malloc(data_len + 256);
	int pos = 0;

	mk_utility::fill_buff_with_word_value_be(app_id, pBuff);
	pos +=2;

	mk_utility::fill_buff_with_word_value_be(offset, pBuff + pos);
	pos += 2;

	int name_len = strlen(file_name);
	mk_utility::fill_buff_with_word_value_be(name_len, pBuff + pos);
	pos += 2;

	memcpy(pBuff + pos, file_name, name_len);
	pos += name_len;

	mk_utility::fill_buff_with_word_value_be(data_len, pBuff + pos);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(app_id, pBuff + pos);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(container_id, pBuff + pos);
	pos += 2;

	mk_utility::fill_buff_with_word_value_be(key_id, pBuff + pos);
	pos += 2;


	memcpy(pBuff + pos, data, data_len);
	pos += data_len;

	newApdu->set_lc_data(pBuff, pos);

	free(pBuff);

	return newApdu;  
}









